#ifndef EMFUNCFILLER_H
#define EMFUNCFILLER_H

#include "FiniteDiff/FuncFiller.h"
#include "Embedded/EmbeddedGrid.h"

template <int Dim>
class EmFuncFiller : public FuncFiller<Dim>
{
public:
  using BaseClass = FuncFiller<Dim>;
  using EmGrid    = EmbeddedGrid<Dim>;

  EmFuncFiller(const EmGrid &aGrid)
      : BaseClass(aGrid), emGrid(aGrid)
  {
  }

  template <class TFunc>
  void fillBdryValues(Tensor<Real, Dim-1> &aData, const char *, const TFunc &expr) const;

  template <class TFunc>
  void fillBdryGrads(Tensor<Real, Dim-1> &aData, const char *, const TFunc &expr) const;

  void transferBdryValues(Tensor<Real, Dim> &aData, const Tensor<Real, Dim-1> &bcData) const {
    doTransferBdryValues(aData, bcData);
  }

  void transferBdryValues(Tensor<Real, Dim> &aData, int) const {
    doTransferBdryValues(aData, [](int) { return 0.0; });
  }

public:
  using BaseClass::fillPointValues;
  using BaseClass::fillBdryValues;

protected:
  template <class T>
  void doTransferBdryValues(Tensor<Real, Dim> &aData, const T &bcData) const;

  const EmGrid &emGrid;
};

//=============================================================
template <int Dim>
template <class TFunc>
void EmFuncFiller<Dim>::fillBdryValues(Tensor<Real, Dim-1> &aData, const char *, const TFunc &expr) const
{
  const auto &bdryPoints = emGrid.bdryPoints;
  const auto &bdryNodes  = emGrid.bdryNodes;
  for(std::size_t i = 0; i < bdryNodes.size(); ++i) {
    auto bp = bdryPoints(bdryNodes[i]);
    aData(i) = expr(bp);
  }
}

template <int Dim>
template <class TFunc>
void EmFuncFiller<Dim>::fillBdryGrads(Tensor<Real, Dim-1> &aData, const char *, const TFunc &expr) const
{
  const auto &bdryPoints = emGrid.bdryPoints;
  const auto &outNormals = emGrid.outNormals;
  const auto &bdryNodes  = emGrid.bdryNodes;
  for(std::size_t i = 0; i < bdryNodes.size(); ++i) {
    auto bp = bdryPoints(bdryNodes[i]);
    auto n  = outNormals(bdryNodes[i]);
    aData(i) = dot(expr(bp), n);
  }
}

template <int Dim>
template <class T>
void EmFuncFiller<Dim>::doTransferBdryValues(Tensor<Real, Dim> &aData, const T &bcData) const
{
  const auto &ghostNodes = emGrid.ghostNodes;
  for(std::size_t i = 0; i < ghostNodes.size(); ++i)
    aData(ghostNodes[i]) = bcData(i);
}

#endif //EMFUNCFILLER_H
